Creating Interactive Blog Posts with WGLMakie.jl

A tutorial on how to add interactive Makie.jl plots to your blog

Wouldn’t it be cool to have 3D interactive visualizations inside blog posts?

From an engagement perspective this would be awesome. Instead of serving static images, we could have interactive plots that the reader can play around with. Potentially to better understand the data or topic at hand, but mostly because it’s fun. Because we don’t want to require any backend services, we need to use a client-side solution. This means serving static HTML files that contain the interactive plots.

So naturally, I thought to myself “I can’t be the first person to think of this”. And ofcourse I wasn’t. I found this great blog post by Aaron which provides a step-by-step guide on how to create interactive plots on a web page using WGLMakie.jl, the web-based backend for the Makie.jl plotting library.

Aaron’s blog post is now 4 years old and the method described there unfortunately no longer works. I was determined, but since I barely know what HTML is I needed a little help. I found out yet again how great, supportive and helpful the Julia community is. After posting on the Julia Discourse I got a response from Simon Danisch who is the creator of Makie.jl.

Note

Besides WGLMakie.jl we will also need Bonito.jl to create the HTML descriptions, which will enable us to embed the plot in a blog post.

A first example

This blog post, which can be read as a tutorial or recipe, is a direct translation from Simon’s response into a working example. I hope it helps you to create interactive plots for your blog posts.

First, we need a location to store the script that generates our plots. I prefer to group all files for a specific post in a single folder. For this post, I created a folder called _posts/guides/interactive-blog/ and saved the script as plots.jl. As a preliminary example we will just plot some random data.

using Bonito, WGLMakie

output_file = "_posts/examples/diffeqviz/sinc_surface.html"

function as_html(io, session, app)
    dom = Bonito.session_dom(session, app)
    show(io, MIME"text/html"(), Bonito.Pretty(dom))
end

session = Session(NoConnection(); asset_server=NoServer())

And the code that generates the plot:

open("_posts/guides/interactive-blog/scatter.html", "w") do io
    println(io, """<center>""")
    app = App() do 
        markersize = Bonito.Slider(range(0.01, stop=0.11, length=6))
        scale_value = DOM.div("\\(s = \\)", markersize.value)

        # Create a scatter plot
        fig, ax = meshscatter(rand(3, 100), markersize=markersize, figure=(; size=(550, 550)))
        
        # Return the plot and the slider
        return Bonito.record_states(session, DOM.div(fig, scale_value, markersize))
    end;
    as_html(io, session, app)
    println(io, """</center>""")
end

The script above creates a scatter plot with random data and saves it as scatter.html. Which we can then include in our blog post using the following liquid:

{% include_relative scatter.html %}

Warning

The HTML files created by `record_states` may be large, since it needs to record all combinations of widget states. This could make your website less responsive. See the documentation for more information.

This will render the scatter plot where the liquid tag is placed:

\(s = \) 0.01

Cool right? Try moving the slider to change the size of the markers. You can also click and drag to rotate the plot.

Warning

We have to pay special attention to the `session` object. The first session will include the setup for any session that’s included afterwards. You’ll also need to follow the order of rendering, since new dependencies get included in the session that first “sees” that dependency.

A DifferentialEquations.jl example

Now for a more exciting example, we will use the DifferentialEquations.jl package to calculate the position of a soccer ball after giving it a kick and use Makie.jl to visualize the resulting trajectory.

We will start from a physical description of the problem. This description is derived from Newton’s second law of motion, which states that the acceleration of an object is directly proportional to the net forces acting on it. An excellent summary of the physics involved can be found in a series of blog posts by Hugo, namely Bend it like Newton: curves in football and The maths behind a chip goal.

−→ F G −→ F L −→ F D −→ v

The trajectory of a soccer ball

The position of a soccer ball in three dimensions can be described by a vector $\vec{x} = [x, y, z]^T$.

Newton’s second law of motion relates the acceleration of the ball to the forces acting on it. Mathematically, this can be written as:

\[\begin{equation} m \frac{d^2 \vec{x}}{dt^2} = \vec{F_G} + \vec{F_D} + \vec{F_L} \end{equation}\]

Where we consider the gravitational force $\vec{F_G}$, the drag force $\vec{F_D}$ and the lift force $\vec{F_L}$. We are interested in solving this equation for $\vec{x}$ because that will give us the position of the ball at any given time. $m$ is the mass of the ball.




Lange, Stefan de (Feb 2025). Creating Interactive Blog Posts with WGLMakie.jl. Hi!. https://langestefan.github.io.

@article{lange2025creating-interactive-blog-posts-with-wglmakie-jl,
  title   = {Creating Interactive Blog Posts with WGLMakie.jl},
  author  = {Lange, Stefan de},journal = {Hi!},
  year    = {2025},
  month   = {Feb},
  url     = {https://langestefan.github.io/blog/2025/interactive-julia-plotting/}
}